package com.interview.rpc.proxy;

import com.interview.rpc.client.RpcClient;
import com.interview.rpc.protocol.Request;
import com.interview.rpc.protocol.Response;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * RPC客户端代理
 * 
 * 使用Java动态代理技术创建服务接口的代理对象
 * 当调用接口方法时，会自动将调用转换为网络请求发送到服务端
 */
public class RpcClientProxy {
    
    // RPC客户端，负责网络通信
    private RpcClient client;
    
    public RpcClientProxy(RpcClient client) {
        this.client = client;
    }
    
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        // 创建动态代理对象
        // clazz.getClassLoader() 获取类加载器
        // new Class[]{clazz} 指定代理要实现的接口
        // new RpcInvocationHandler() 指定方法调用处理器
        return (T) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class[]{clazz},
                new RpcInvocationHandler()
        );
    }
    
    /**
     * RPC调用处理器
     * 
     * 实现InvocationHandler接口，处理代理对象的方法调用
     */
    private class RpcInvocationHandler implements InvocationHandler {
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // Object类的方法（如toString、hashCode等）不进行远程调用，直接本地执行
            if (Object.class == method.getDeclaringClass()) {
                try {
                    return method.invoke(this, args);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
                return null;
            }
            
            // 构造RPC请求对象
            Request request = new Request();
            // 生成唯一请求ID
            request.setRequestId(UUID.randomUUID().toString());
            // 获取要调用的类名（接口名）
            request.setClassName(method.getDeclaringClass().getName());
            // 获取要调用的方法名
            request.setMethodName(method.getName());
            // 获取方法参数类型列表
            request.setParameterTypes(method.getParameterTypes());
            // 获取方法参数值列表
            request.setParameters(args);
            
            // 通过RPC客户端发送请求并获取响应
            Response response = client.sendRequest(request);
            
            // 检查调用是否成功
            if (response.getCode() != 200) {
                throw new RuntimeException("RPC调用失败: " + response.getMessage());
            }
            
            // 返回调用结果
            return response.getData();
        }
    }
}